using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
using System.Xml;
using HMCollada.Core;
using System;

namespace HMCollada.Core {
    internal struct HMColladaNode {
        internal string id;
        internal string name;
        internal string sid;
        internal HMColladaNodeType type;
        internal string layer;
        internal HMColladaAsset asset;
        internal Collection<float[]> lookat;
        internal Collection<Matrix> matrix;
        internal Collection<Quaternion> rotate;
        internal Collection<Vector3> scale;
        internal Collection<float[]> skew;
        internal Collection<Vector3> translate;
        internal Collection<HMColladaInstanceGeometry> instance_geometry;
        internal Collection<HMColladaNode> node;
    }

    internal enum HMColladaNodeType {
        NODE,
        JOINT
    }
}

namespace HMCollada {
    internal partial class HMColladaLoader {
        private static HMColladaNode LoadNode() {
            HMColladaNode newNode = new HMColladaNode();
            string type = colladaReader.GetAttribute("type");
            newNode.id = colladaReader.GetAttribute("id");
            newNode.name = colladaReader.GetAttribute("name");
            newNode.sid = colladaReader.GetAttribute("sid");
            newNode.layer = colladaReader.GetAttribute("layer");
            newNode.type = type == "JOINT" ? HMColladaNodeType.JOINT : HMColladaNodeType.NODE;

            newNode.lookat = new Collection<float[]>();
            newNode.matrix = new Collection<Matrix>();
            newNode.rotate = new Collection<Quaternion>();
            newNode.scale = new Collection<Vector3>();
            newNode.skew = new Collection<float[]>();
            newNode.translate = new Collection<Vector3>();
            newNode.instance_geometry = new Collection<HMColladaInstanceGeometry>();
            newNode.node = new Collection<HMColladaNode>();

            if (!colladaReader.IsEmptyElement) colladaReader.Read();
            do {
                if (colladaReader.NodeType != XmlNodeType.EndElement) {
                    switch (colladaReader.Name) {
                        case "asset": newNode.asset = LoadAsset(); break;
                        case "lookat": newNode.lookat.Add(LoadLookAt()); break;
                        case "matrix": newNode.matrix.Add(LoadMatrix()); break;
                        case "rotate": newNode.rotate.Add(LoadRotate()); break;
                        case "scale": newNode.scale.Add(LoadScale()); break;
                        case "skew": newNode.skew.Add(LoadSkew()); break;
                        case "translate": newNode.translate.Add(LoadTranslate()); break;
                        case "instance_geometry": newNode.instance_geometry.Add(LoadInstanceGeometry()); break;
                        case "node": newNode.node.Add(LoadNode()); break;
                        case "instance_light": colladaReader.Read(); break; // skip light objects
                        case "instance_camera": colladaReader.Read(); break; // skip camera objects
                    }
                }

                if (!colladaReader.IsEmptyElement) colladaReader.Read();
            } while (!(colladaReader.Name == "node" && colladaReader.NodeType == XmlNodeType.EndElement));

            if (colladaReader.IsEmptyElement) colladaReader.Read();
            return newNode;
        }

        private static Vector3 LoadTranslate() {
            float[] values = ReadGenericFloatArray(3);

            return new Vector3(values[0], values[1], values[2]);
        }

        private static float[] LoadSkew() {
            return ReadGenericFloatArray(7);
        }

        private static Vector3 LoadScale() {
            float[] values = ReadGenericFloatArray(3);

            return new Vector3(values[0], values[1], values[2]);
        }

        private static Quaternion LoadRotate() {
            float[] values = ReadGenericFloatArray(4);

            Quaternion q = Quaternion.CreateFromAxisAngle(
                new Vector3(
                    values[0],
                    values[1],
                    values[2]
                ),
                (float)(values[3] * Math.PI / 180.0f)
            );

            return q;
        }

        private static Matrix LoadMatrix() {
            float[] values = ReadGenericFloatArray(16);
            Matrix m = new Matrix();

            m.M11 = values[0]; m.M12 = values[1]; m.M13 = values[2]; m.M14 = values[3];
            m.M21 = values[4]; m.M22 = values[5]; m.M23 = values[6]; m.M24 = values[7];
            m.M31 = values[8]; m.M32 = values[9]; m.M33 = values[10]; m.M34 = values[11];
            m.M41 = values[12]; m.M42 = values[13]; m.M43 = values[14]; m.M44 = values[15];

            return m;
        }

        private static float[] LoadLookAt() {
            return ReadGenericFloatArray(9);
        }
    }
}